﻿using System;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using System.Linq;

namespace FBGraph.ClientGenerator.CodeGen
{
    public class Property : Member
    {
        public Property(String name, Type type) : this(name, new TypeReference(type)) { }

        public Property(String name, TypeReference type)
        {
            this.Name = name;
            this.BaseType = type;
            this.Accessibility = "public";
        }

        public String Name { get; set; }
        public TypeReference BaseType { get; set; }
        public String Accessibility { get; set; }
        public virtual String GetBody { get; set; }
        public virtual String SetBody { get; set; }
        public Boolean HasGet { get; set; }
        public Boolean HasSet { get; set; }
        public String GetAccessibility { get; set; }
        public String SetAccessibility { get; set; }
        public TypeReference ExplicitImplementationType { get; set; }
        public Boolean Override { get; set; }

        public override Boolean Equals(Object obj)
        {
            var that = obj as Property;
            if (that == null) return false;
            else return
                this.Name == that.Name &&
                this.ExplicitImplementationType == that.ExplicitImplementationType;
        }

        public override Int32 GetHashCode()
        {
            var hashCode = this.Name.GetHashCode();
            if (this.ExplicitImplementationType != null) hashCode ^= this.ExplicitImplementationType.GetHashCode();
            return hashCode;                
        }

        protected override void WriteDeclaration(CodeGenWriter writer)
        {
            if (this.ExplicitImplementationType == null)
            {
                writer.Write(this.Accessibility);
                writer.Write(' ');
            }

            writer.Write(this.BaseType);
            writer.Write(' ');

            if (this.ExplicitImplementationType != null)
            {
                writer.Write(this.ExplicitImplementationType);
                writer.Write('.');
            }

            writer.Write(this.Name);
        }

        protected override void WriteOpenBody(CodeGenWriter writer)
        {
            if ((this.HasGet && !String.IsNullOrEmpty(this.GetBody)) ||
                (this.HasSet && !String.IsNullOrEmpty(this.SetBody)))
            {
                writer.WriteLine();
                base.WriteOpenBody(writer);
            }
            else writer.Write(" { ");
        }

        protected override void WriteBody(CodeGenWriter writer)
        {
            WriteGetStatement(writer);
            WriteSetStatement(writer);
        }

        protected virtual void WriteStatement(CodeGenWriter writer, String getOrSet, String accessibility, String body)
        {
            if (!String.IsNullOrEmpty(accessibility) && accessibility != this.Accessibility)
            {
                writer.Write(accessibility);
                writer.Write(' ');
            }
            writer.Write(getOrSet);
            if (body == null)
            {
                writer.Write("; ");
            }
            else if (body == String.Empty)
            {
                writer.WriteLine(" { }");
            }
            else
            {
                writer.WriteLine();
                writer.WriteLine('{');
                using (writer.Indent())
                {
                    writer.WriteLine(body);
                }
                writer.WriteLine('}');
            }
        }

        protected virtual void WriteGetStatement(CodeGenWriter writer)
        {
            if(this.HasGet) WriteStatement(writer, "get", this.GetAccessibility, this.GetBody);
        }

        protected virtual void WriteSetStatement(CodeGenWriter writer)
        {
            if(this.HasSet) WriteStatement(writer, "set", this.SetAccessibility, this.SetBody);
        }

        public void MakeNullable()
        {
            if (this.BaseType.Type != null && this.BaseType.Type.IsValueType && (!this.BaseType.Type.IsGenericType || this.BaseType.Type.GetGenericTypeDefinition() != typeof(Nullable<>)))
            {
                this.BaseType = new TypeReference(typeof(Nullable<>), this.BaseType);
            }
        }
    }
}